package com.angel.email.configuration;

import java.util.Properties;

import javax.mail.Authenticator;
import javax.mail.MessagingException;
import javax.mail.NoSuchProviderException;
import javax.mail.Session;
import javax.mail.Store;

import com.angel.common.keyValue.KeyValueResult;
import com.angel.common.keyValue.impl.KeyValueResultImpl;
import com.angel.email.account.AccountInformation;
import com.angel.email.application.ApplicationEmailConnections;
import com.angel.email.authenticator.EmailAuthenticator;
import com.angel.email.connection.EmailConnection;
import com.angel.email.connection.FullEmailConnection;
import com.angel.email.connection.IncomingEmailConnection;
import com.angel.email.connection.OutgoingEmailConnection;
import com.angel.email.constants.EmailPropertyConstants;
import com.angel.email.exceptions.EmailAccessException;
import com.angel.email.provider.ProviderType;

/**
 *
 * @author William
 *
 */
public class EmailConfiguration {

	private KeyValueResult properties;
	private AccountInformation incomingAccountInformation;
	private AccountInformation outgoingAccountInformation;

	public EmailConfiguration(AccountInformation incomingAccountInformation, AccountInformation outgoingAccountInformation){
		this();
		this.setIncomingAccountInformation(incomingAccountInformation);
		this.setOutgoingAccountInformation(outgoingAccountInformation);
	}

	public EmailConfiguration(){
		super();
		this.setProperties(new KeyValueResultImpl());
	}

	/**
	 * @return the properties
	 */
	protected KeyValueResult getProperties() {
		return properties;
	}

	/**
	 * @param properties the properties to set
	 */
	protected void setProperties(KeyValueResult properties) {
		this.properties = properties;
	}

	public void addProperty(String name, String value){
		this.getProperties().addKeyValue(name, value);
	}

	public void removeProperty(String name){
		this.getProperties().removeKeyValue(name);
	}

	public void containsProperty(String name){
		this.getProperties().containsResultFor(name);
	}
	
	public void addKeyValueResult(KeyValueResult keyValueResult){
		this.getProperties().addKeyValueResult(keyValueResult);
	}

	public Properties buildProperties(){
		Properties properties = new Properties();
		properties.clear();
		for(Object key: this.getProperties().findAllKeys()){
			properties.put(key, this.getProperties().getResultFor(key));
		}
		return properties;
	}

	public FullEmailConnection connectToFullEmail(){
		OutgoingEmailConnection outgoingEmailConnection = this.connectToOutgoingEmail();
		IncomingEmailConnection incomingEmailConnection = this.connectToIncomingEmail();
		return new FullEmailConnection(incomingEmailConnection, outgoingEmailConnection);
	}
	
	public OutgoingEmailConnection connectToOutgoingEmail(){
		Properties properties = this.buildProperties();
		Session sesion = this.preConnection(properties, new EmailAuthenticator(outgoingAccountInformation));
		Store store;
		OutgoingEmailConnection outgoingEmailConnection = null;
		ProviderType providerType = this.findProviderType(properties);
		String outgogingUrl = this.findEmailOutgoingServerURL(properties);
		String connectionName = this.getOutgoingAccountInformation().getName();
		try {
			store = sesion.getStore(providerType.getProvider());
			store.connect(outgogingUrl, null, null);
			outgoingEmailConnection = new OutgoingEmailConnection(store, connectionName);
		} catch (NoSuchProviderException e) {
			throw new EmailAccessException("Error getting provider [" + providerType.getProvider() +  "].", e);
		} catch (MessagingException e) {
			throw new EmailAccessException("Error connecting with server [" + "].", e);
		}
		this.addConnectionToCache(outgoingEmailConnection);
		return outgoingEmailConnection;
	}
	
	protected void addConnectionToCache(EmailConnection emailConnection) {
		ApplicationEmailConnections emailsConnections = ApplicationEmailConnections.createInstance();
		if(emailsConnections.containsEmailConnection(emailConnection)){
			emailsConnections.addEmailConnection(emailConnection);
		}
	}

	private String findEmailOutgoingServerURL(Properties properties) {
		String emailServerURL = properties.getProperty(EmailPropertyConstants.EMAIL_OUTGOING_URL);
		if(emailServerURL  == null){
			throw new EmailAccessException("Email server url cannot be null. It must be setted and it depends on provider type.");
		}
		return emailServerURL;
	}

	public IncomingEmailConnection connectToIncomingEmail(){
		Properties properties = this.buildProperties();
		Session sesion = this.preConnection(properties, new EmailAuthenticator(incomingAccountInformation));

		Store store;
		IncomingEmailConnection emailConnection = null;
		ProviderType providerType = this.findProviderType(properties);
		String incomingUrl = this.findEmailIncomingServerURL(properties);
		String connectionName = incomingAccountInformation.getName();
		try {
			store = sesion.getStore(providerType.getProvider());
			store.connect(incomingUrl, null, null);
			emailConnection = new IncomingEmailConnection(store, connectionName);
		} catch (NoSuchProviderException e) {
			throw new EmailAccessException("Error getting provider [" + providerType.getProvider() +  "].", e);
		} catch (MessagingException e) {
			throw new EmailAccessException("Error connecting with server [" + "].", e);
		}
		return emailConnection;
	}
	
	protected Session preConnection(Properties properties, Authenticator authenticator){
		Boolean debug = Boolean.valueOf(
				this.getProperties().containsResultFor(EmailPropertyConstants.EMAIL_DEBUG) ?
				this.getProperties().getResultFor(EmailPropertyConstants.EMAIL_DEBUG).toString() :
				EmailPropertyConstants.EMAIL_DEBUG_ENABLED.toString()
				);

		Session session = Session.getInstance(properties, authenticator);
		session.setDebug(debug);
		return session;
	}

	private String findEmailIncomingServerURL(Properties properties) {
		String emailServerURL = properties.getProperty(EmailPropertyConstants.EMAIL_INCOMING_URL);
		if(emailServerURL  == null){
			throw new EmailAccessException("Email server url cannot be null. It must be setted and it depends on provider type.");
		}
		return emailServerURL;
	}

	private ProviderType findProviderType(Properties properties) {
		String providerType = properties.getProperty(EmailPropertyConstants.EMAIL_PROVIDER);
		if(providerType == null){
			throw new EmailAccessException("Provider type cannot be null. It must be setted [For example: pop3, or imap].");
		}
		return ProviderType.findProviderType(providerType);
	}

	/**
	 * @return the incomingAccountInformation
	 */
	public AccountInformation getIncomingAccountInformation() {
		return incomingAccountInformation;
	}

	/**
	 * @param incomingAccountInformation the incomingAccountInformation to set
	 */
	public void setIncomingAccountInformation(
			AccountInformation incomingAccountInformation) {
		this.incomingAccountInformation = incomingAccountInformation;
	}

	/**
	 * @return the outgoingAccountInformation
	 */
	public AccountInformation getOutgoingAccountInformation() {
		return outgoingAccountInformation;
	}

	/**
	 * @param outgoingAccountInformation the outgoingAccountInformation to set
	 */
	public void setOutgoingAccountInformation(
			AccountInformation outgoingAccountInformation) {
		this.outgoingAccountInformation = outgoingAccountInformation;
	}
}
